package com.douqu.game.battle.server;


import com.bean.core.buffer.ByteBuffer;
import com.douqu.game.battle.route.RouteObject;
import com.douqu.game.battle.server.netty.NettyTCPClient;
import com.douqu.game.battle.server.netty.NettyTCPServer;
import com.douqu.game.core.factory.*;
import com.douqu.game.core.i.IMainServer;
import com.douqu.game.core.i.IServer;
import com.douqu.game.core.util.LogUtils;
import com.douqu.game.core.util.SendUtils;
import io.netty.channel.Channel;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;


/**
 * Author : Bean
 * 2017-06-19 18:39
 */
public class GameServer {

    /**
     * 是否在更新class中
     */
    public static boolean updating = false;
//    /**
//     * 寻路线程
//     */
//    private RouteThread routeThread;
    /**
     * 服务器连接
     */
    private IServer tcpServer;


    /**
     * 连接主服务器的
     */
    private NettyTCPClient tcpClient;

    /**
     * RMI方式连接主服务器
     */
    private IMainServer iMainServer;

    /**
     * 连接主服务器的通道
     */
    private Channel serverChannel;


    private int tcpPort;

    private int routePort;

    /**
     * 服务器开启时间
     */
    private long startTime;

    /**
     * 可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程
     */
    private ExecutorService service = Executors.newCachedThreadPool();

    /**
     * 定长线程池，支持定时及周期性任务执行,用来处理战斗计算
     */
    private ScheduledExecutorService updateBattleService = Executors.newScheduledThreadPool(1);

    /**
     * 寻路线程
     */
    private ExecutorService routeService = Executors.newSingleThreadExecutor();

    private WorldManager worldManager;

    private static GameServer instance = null;

    private GameServer() {
        worldManager = new WorldManager();
    }

    public static GameServer getInstance()
    {
        if(instance == null)
            instance = new GameServer();

        return instance;
    }

    public void start()
    {
        if(tcpClient == null)
        {
            LoadFactory.loadConfig();

            startThread(() -> {
                tcpClient = new NettyTCPClient();
                tcpClient.start();
            });
        }
    }

    public void startThread(Runnable runnable)
    {
        service.execute(runnable);
    }




    public void startBattleTCP(int port)
    {
        this.tcpPort = port;

        if(tcpServer == null)
        {
//            startThread(() -> {
                tcpServer = new NettyTCPServer();
                tcpServer.start();
//            });
        }
    }


    public void startRouteServer()
    {
//        if(routeThread == null)
//        {
//            routeThread = new RouteThread();
//            startThread(routeThread);
//        }
    }


    public void startRMI()
    {
        try {
            //在RMI服务注册表中查找名称为X的对象，并调用其上的方法

            LogUtils.info("连接RMI:" + ConfigFactory.SERVER_URL_RMI);

            iMainServer = (IMainServer) Naming.lookup(ConfigFactory.SERVER_URL_RMI);

            LogUtils.info("RMI 链接成功: " + ConfigFactory.SERVER_URL_RMI);

            DataFactory.getInstance().set(iMainServer.getDataFactory());

        } catch (NotBoundException e) {
            e.printStackTrace();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void startUpdate()
    {
        LogUtils.info("开启游戏更新线程---------------------->");

        startTime = System.currentTimeMillis();

        //每500毫秒更新计算一次,不管上次计算是否完成
        updateBattleService.scheduleAtFixedRate(() -> {

            try{
                DataFactory.currentTime = System.currentTimeMillis();

                if(updating)
                {
                    LogUtils.warn("正在更新Class，暂停WorldManager的update");
                }
                else
                {
                    GameServer.getInstance().getWorldManager().update();
                }

            }catch (Exception e){
                LogUtils.errorException(e);
            }

        }, 1000, ConstantFactory.UPDATE_BATTLE_TIME, TimeUnit.MILLISECONDS);

//        //每60秒向主服务器同步一次战斗数据
//        updateBattleService.scheduleAtFixedRate(()->{
//
//            worldManager.sendBattleData(null);
//
//        }, 2, 60, TimeUnit.SECONDS);
    }



    public void stop()
    {
        service.shutdown();
        updateBattleService.shutdown();

        if(tcpClient != null)
        {
            tcpClient.stop();
            tcpClient = null;
        }
        if(tcpServer != null)
        {
            tcpServer.stop();
            tcpServer = null;
        }

        System.exit(0);
    }


    public void pingToMain(String battleId)
    {
        ByteBuffer out = new ByteBuffer();
        out.writeInt(getId());
        out.writeUTF(battleId);
        SendUtils.sendMsg(serverChannel, CodeFactory.BATTLE_PING_MAIN, out.getBytes());
    }

    public void findRoute(RouteObject routeObject)
    {
        LogUtils.info("开始寻路  -> " + routeObject);
        routeService.execute(() ->{
            LogUtils.info("寻路线程开始执行 -------" + routeService.toString());
            try{
                routeObject.findRoute();
            }catch (Exception e){
                e.printStackTrace();
                LogUtils.errorException(e);
            }
            LogUtils.info("寻路线程执行完成 -------");
        });
    }


    public boolean isRunning()
    {
        return tcpServer != null;
    }

    public int getId()
    {
        return tcpPort;
    }


    public Channel getServerChannel() {
        return serverChannel;
    }

    public void setServerChannel(Channel serverChannel) {
        this.serverChannel = serverChannel;
    }


    public int getTcpPort() {
        return tcpPort;
    }

    public void setTcpPort(int tcpPort) {
        this.tcpPort = tcpPort;
    }

    public int getRoutePort() {
        return routePort;
    }

    public void setRoutePort(int routePort) {
        this.routePort = routePort;
    }



    public WorldManager getWorldManager() {
        return worldManager;
    }

    public IMainServer getIMainServer() {
        return iMainServer;
    }

//    public RouteThread getRouteThread() {
//        return routeThread;
//    }
}
